package com.bianmaba.spring.security.handler;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Controller()
public class AjaxSecurityHandler extends AbstractAjaxHandler implements AuthenticationSuccessHandler, LogoutSuccessHandler, AuthenticationFailureHandler, AccessDeniedHandler, SessionInformationExpiredStrategy {

    private static final Logger LOGGER = LoggerFactory.getLogger(AjaxSecurityHandler.class);

    @RequestMapping("not_logon")
    public void notLogon(HttpServletResponse response) throws IOException {
        Map<String, Object> result = new HashMap<String, Object>(0);
        result.put("success", false);
        result.put("status", 401.1);
        result.put("statusText", "Not logon");
        result.put("SecurityMessage", true);
        result.put("result", "资源请求失败，请求的资源需要在您登录后才能访问！");
        writeAjax(response, result, 401);
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        UserDetails principal = (UserDetails) authentication.getPrincipal();
        Map<String, Object> result = new HashMap<String, Object>(0);
        result.put("success", true);
        result.put("SecurityMessage", true);
        result.put("result", "用户登录成功");
        result.put("data", principal);
        writeAjax(response, result, 200);
        LOGGER.debug(result.get("result").toString());
    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        UserDetails principal = (UserDetails) authentication.getPrincipal();
        Map<String, Object> result = new HashMap<String, Object>(0);
        result.put("success", true);
        result.put("SecurityMessage", true);
        result.put("result", "用户注销成功");
        result.put("data", principal);
        writeAjax(response, result, 200);
        LOGGER.debug(result.get("result").toString());
    }


    /**
     * 处理用户验证错误的错误信息
     *
     * @param request
     * @param response
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        String resultStr = null;

        if (e.getClass().isAssignableFrom(UsernameNotFoundException.class) || e.getClass().isAssignableFrom(BadCredentialsException.class)) {
            resultStr = "登录失败，用户名或密码错误，请检查用户名密码是否输入正确，键盘大写锁定状态！";
        } else if (e.getClass().isAssignableFrom(AccountExpiredException.class)) {
            resultStr = "登录失败，用户帐号已经过期，请联系管理员！";
        } else if (e.getClass().isAssignableFrom(AuthenticationCredentialsNotFoundException.class)) {
            resultStr = "登录失败，用户帐号证书不存在，请联系管理员！";
        } else if (e.getClass().isAssignableFrom(AuthenticationCredentialsNotFoundException.class)) {
            resultStr = "登录失败，用户帐号证书过期，请联系管理员！";
        } else if (e.getClass().isAssignableFrom(LockedException.class)) {
            resultStr = "登录失败，用户帐号已被锁定，请联系管理员！";
        } else if (e.getClass().isAssignableFrom(DisabledException.class)) {
            resultStr = "登录失败，用户帐号已被禁用，请联系管理员！";
        } else if (e.getClass().isAssignableFrom(AccountStatusException.class)) {
            resultStr = "登录失败，用户帐号状态异常，请联系管理员！";
        } else if (e.getClass().isAssignableFrom(InternalAuthenticationServiceException.class)) {
            resultStr = "登录失败，认证服务内部错误，请联系管理员！";
        }
        Map<String, Object> result = new HashMap<String, Object>(0);
        result.put("success", false);
        result.put("status", 401);
        result.put("statusText", e.getLocalizedMessage());
        result.put("SecurityMessage", true);
        result.put("result", resultStr);
        result.put("failureType", e.getClass().getSimpleName());
        result.put("exception", ExceptionUtils.getStackTrace(e));
        writeAjax(response, result, 401);
        LOGGER.error(result.get("result").toString(), e);
    }

    /**
     * 处理无授权时的错误信息
     *
     * @param httpServletRequest
     * @param response
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        Map<String, Object> result = new HashMap<String, Object>(0);
        result.put("success", false);
        result.put("status", 403);
        result.put("statusText", e.getLocalizedMessage());
        result.put("securityMessage", true);
        result.put("result", "资源请求失败，当前用户没有取得该资源的授权，请联系管理员授权！");
        result.put("exception", ExceptionUtils.getStackTrace(e));
        result.put("failureType", e.getClass().getSimpleName());
        writeAjax(response, result, 403);
        LOGGER.error(result.get("result").toString(), e);
    }

    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
        HttpServletResponse response = event.getResponse();
        Map<String, Object> result = new HashMap<String, Object>(0);
        result.put("success", false);
        result.put("status", 401.1);
        result.put("statusText", "Session expired");
        result.put("securityMessage", true);
        result.put("result", "资源请求失败，当前用户已经在其它地方登录！");
        result.put("exception", null);
        result.put("failureType", "SessionExpired");
        writeAjax(response, result, 401);
    }
}
